// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... namespace LargoCommon.Music { using Abstract; using JetBrains.Annotations; using Localization; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; /// /// Musical Block Model. /// [Serializable] public sealed class HarmonicModel : AbstractModel, ICloneable { #region Fields /// /// Harmonic motives. /// [NonSerialized] private IEnumerable harmonicMotives; /// /// The harmonic bars /// private IEnumerable harmonicBars; #endregion #region Constructors /// /// Initializes a new instance of the class. /// public HarmonicModel() { this.HarmonicMotives = new List(); } #endregion #region Public Properties /// /// Gets the harmonic streams. /// /// /// The harmonic streams. /// [UsedImplicitly] public IList HarmonicStreams { get { var list = new List(); var motives = this.HarmonicMotives; foreach (var motive in motives) { if (motive.Length <= 8) { continue; } list.Add(motive.HarmonicStream); } return list; } } /// /// Gets or sets the harmonic motives. /// /// /// The harmonic motives. /// public IEnumerable HarmonicMotives { get { Contract.Ensures(Contract.Result>() != null); if (this.harmonicMotives == null) { throw new InvalidOperationException("Harmonic motives are null."); } return this.harmonicMotives; } set => this.harmonicMotives = value ?? throw new ArgumentException(LocalizedMusic.String("Argument cannot be empty."), nameof(value)); } /// /// Gets or sets the rhythmic structures. /// /// Property description. public IEnumerable HarmonicBars { get { Contract.Ensures(Contract.Result>() != null); if (this.harmonicBars != null) { return this.harmonicBars; } var rs = new List(); foreach (var m in this.HarmonicMotives) { rs.AddRange(m.HarmonicStream.HarmonicBars); } this.harmonicBars = rs; //// var rsd = (from r in rs select r).Distinct().OrderBy(x => x.Length).ToList(); ////. x.ElementSchema); //// return rsd; return rs; } set => this.harmonicBars = value; } #endregion #region Other public properties /// /// Gets the first melodic bar. /// /// Property description. [UsedImplicitly] public int FirstMelodicBar { get { var barNumber = (from c in this.BlockChanges.Changes where c.IsMelodicalNature orderby c.BarNumber select c.BarNumber).FirstOrDefault(); return barNumber; } } #endregion #region Static factory methods /// /// Extracts the musical block model. /// /// The musical block. /// /// Returns value. /// public static HarmonicModel GetNewModel(MusicalBlock musicalBlock) { musicalBlock.Header.NumberOfLines = (byte)musicalBlock.Strip.Lines.Count; var model = GetNewModel(musicalBlock.Header.Name, musicalBlock); model.Number = musicalBlock.Header.Number; model.SourceMusicalBlock = musicalBlock; //// model.Core = new MusicalCore(musicalBlock.Header); model.Header = musicalBlock.Header; ProcessLogger.Singleton.SendLogEvent(null, LocalizedMusic.String("Analyzing tempo..."), 0); //// musicalBlock.Name var tempoChanges = musicalBlock.AnalyzeTempoChanges(); tempoChanges.ForAll(change => model.BlockChanges.Changes.Add(change)); ProcessLogger.Singleton.SendLogEvent(null, LocalizedMusic.String("Analyzing harmony..."), 0); //// musicalBlock.Name var harmonicAnalysis = MusicalSettings.Singleton.HarmonicAnalysis; //// HarmonicAnalyzeType.DivisionByTicks var harmonicAnalyzer = new HarmonicAnalyzer(musicalBlock.Body); var harmonicChanges = harmonicAnalyzer.AnalyzeHarmony(model, harmonicAnalysis); harmonicChanges.ForAll(change => model.BlockChanges.Changes.Add(change)); //// 2016 this.Body.MakeBars(model.Header.NumberOfBars, this.Header); model.SourceMusicalBlock.NumberOfBars //// 2019/12 !? musicalBlock.Body.SetHarmonicBasis(model.BlockChanges); //// true return model; } /// /// Gets the new musical model. /// /// Name of the model. /// The musical block. /// /// Returns value. /// /// Null Exception. public static HarmonicModel GetNewModel(string modelName, MusicalBlock musicalBlock) { Contract.Requires(musicalBlock != null); var model = new HarmonicModel { Name = modelName, IsSelected = false }; if (model == null) { throw new ArgumentNullException(nameof(modelName)); } model.Header = musicalBlock.Header; model.SourceMusicalBlock = musicalBlock; return model; } #endregion #region Public methods /// /// Rhythmic of harmony. /// /// Returns value. [UsedImplicitly] public IList RhythmicOfHarmony() { var structs = new List(); var bars = this.HarmonicBars; // ReSharper disable once LoopCanBeConvertedToQuery foreach (var harBar in bars) { var rstruct = harBar.RhythmicStructure; //// Clone? structs.Add(rstruct); } return structs; } /// /// Gets the harmonic motive. /// /// The number. /// Returns value. [UsedImplicitly] public HarmonicMotive GetHarmonicMotive(int number) { var cnt = this.HarmonicMotives.Count(); if (cnt == 0) { return null; } var localNumber = number; if (localNumber > cnt) { checked { localNumber = ((number - 1) % cnt) + 1; } } //// HarmonicMotive motive = this.HarmonicMotives.ElementAt(localNumber); var motive = (from m in this.HarmonicMotives where m.Number == localNumber select m).FirstOrDefault(); return motive; } /// /// Adds the motive. /// /// The motive. public void AddMotive(HarmonicMotive motive) { Contract.Requires(motive != null); //// motive.Core = this; motive.Recompute(); ((List)this.HarmonicMotives).Add(motive); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// [UsedImplicitly] public override string ToString() { var system = this.Header.System; return system.HarmonicOrder.ToString(CultureInfo.InvariantCulture); } #endregion /// Makes a deep copy of the BlockModel object. /// Returns object. public object Clone() { var model = new HarmonicModel { Name = this.Name, Number = this.Number, //// Core = this.Core, Header = (MusicalHeader)this.Header.Clone(), BlockChanges = new MusicalChanges() }; model.BlockChanges.AppendCopyOfChanges(this.BlockChanges.Changes); //// model model.SourceMusicalBlock = this.SourceMusicalBlock; //// model.MakeBars(model.NumberOfBars); return model; } #region Material Extractor /// /// Extract harmonic material. /// /// /// Returns value. /// public HarmonicMaterial ExtractHarmonicMaterial() { var system = this.Header.System; var thm = new HarmonicMaterial { Name = this.Name, HarmonicOrder = system.HarmonicOrder }; //// dcm.AddToTHarmonicMaterial(thm); var list = this.ListOfStructures(); var groupList = (from ms in list group ms by ms.GetStructuralCode into g select g).ToList(); groupList.ForEach(g => { var hs = g.FirstOrDefault(); if (hs == null) { return; } var mhs = hs; //// clone? var chs = hs.GetClassStructure; hs.ClassCode = chs.GetStructuralCode; hs.Occurrence = g.Count(); thm.Structures.Add(mhs); }); //// dcm.SaveChanges(); return thm; } #endregion /// /// Lists the of structures. /// /// /// Returns value. /// private IEnumerable ListOfStructures() { var list = new Collection(); foreach (var structure in from motive in this.HarmonicMotives from bar in motive.HarmonicStream.HarmonicBars from structure in bar.HarmonicStructures let sc = structure.GetStructuralCode where !string.IsNullOrEmpty(sc) select structure) { list.Add(structure); } return list; } } }